package com.fushenlan.epidemicControl.config;

import cn.hutool.core.date.DateUtil;
import cn.hutool.core.lang.UUID;
import cn.hutool.jwt.JWTPayload;
import cn.hutool.jwt.JWTUtil;
import cn.hutool.jwt.signers.JWTSignerUtil;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import javax.servlet.http.HttpServletRequest;
import java.nio.charset.StandardCharsets;
import java.util.Map;

/**
 * @author Administrator
 * @date 2022/5/2 2:18
 */
@Component
@Slf4j
public class JwtProvider {

    @Autowired
    JwtProperties jwtProperties;


    public String resolveToken(HttpServletRequest request) {
        String bearerToken = request.getHeader(jwtProperties.getHeader());
        log.info("resolve token from header is :{}", bearerToken);
        if (bearerToken != null && bearerToken.startsWith(jwtProperties.getTokenPrefix())) {
            return bearerToken.substring(jwtProperties.getTokenPrefix().length() + 1);
        }
        return null;
    }

    /**
     * 创建TOKEN
     *
     * @param payLoad
     * @return
     */
    public String createToken(Map<String, Object> payLoad) {
        String now = DateUtil.now();
        payLoad.put(JWTPayload.ISSUED_AT, now);
        //过期时间
        payLoad.put(JWTPayload.EXPIRES_AT, DateUtil.offsetSecond(DateUtil.date(), jwtProperties.getExpireTime()).toString("yyyy-MM-dd HH:mm:ss"));
        //生效时间
        payLoad.put(JWTPayload.NOT_BEFORE, now);
        //唯一id
        String jwtId = UUID.fastUUID().toString();
        payLoad.put(JWTPayload.JWT_ID, jwtId);

        return jwtProperties.getTokenPrefix() + " " + JWTUtil.createToken(payLoad, JWTSignerUtil.hs256(jwtProperties.getSecret().getBytes(StandardCharsets.UTF_8)));
    }

    /**
     * 创建TOKEN
     *
     * @param payLoad
     * @param key     和secret一起签名
     * @return
     */
    public String createToken(Map<String, Object> payLoad, String key) {
        String now = DateUtil.now();
        payLoad.put(JWTPayload.ISSUED_AT, now);
        //过期时间
        payLoad.put(JWTPayload.EXPIRES_AT, DateUtil.offsetSecond(DateUtil.date(), jwtProperties.getExpireTime()).toString("yyyy-MM-dd HH:mm:ss"));
        //生效时间
        payLoad.put(JWTPayload.NOT_BEFORE, now);
        //唯一id
        String jwtId = UUID.fastUUID().toString();
        payLoad.put(JWTPayload.JWT_ID, jwtId);

        return jwtProperties.getTokenPrefix() + " " + JWTUtil.createToken(payLoad, JWTSignerUtil.hs256((key + jwtProperties.getSecret()).getBytes(StandardCharsets.UTF_8)));
    }


    /**
     * 验证token
     *
     * @param token
     */
    public boolean validateToken(String token) {
        String replace = token.replace(jwtProperties.getTokenPrefix(), "");
        boolean verify = JWTUtil.verify(replace, JWTSignerUtil.hs256(jwtProperties.getSecret().getBytes(StandardCharsets.UTF_8)));
        if (!verify) {
            return false;
        }
        //验证jwt是不是过期了
        JWTPayload jwtPayload = JWTUtil.parseToken(replace).getPayload();
        String expire = jwtPayload.getClaim(JWTPayload.EXPIRES_AT) + "";
        if (DateUtil.compare(DateUtil.parseDateTime(expire), DateUtil.date()) < 0) {
            log.info("token has expired! the expire time:{}", expire);
            return false;
        }
        return true;
    }

    /**
     * 验证token
     *
     * @param token
     * @param key
     */
    public boolean validateToken(String token, String key) {
        String replace = token.replace(jwtProperties.getTokenPrefix(), "");
        boolean verify = JWTUtil.verify(replace, JWTSignerUtil.hs256((key + jwtProperties.getSecret()).getBytes(StandardCharsets.UTF_8)));
        if (!verify) {
            return false;
        }
        //验证jwt是不是过期了
        JWTPayload jwtPayload = JWTUtil.parseToken(replace).getPayload();
        String expire = jwtPayload.getClaim(JWTPayload.EXPIRES_AT) + "";
        if (DateUtil.compare(DateUtil.parseDateTime(expire), DateUtil.date()) < 0) {
            log.info("token has expired! the expire time:{}", expire);
            return false;
        }
        return true;
    }

    /**
     * 检查token是否需要更新
     *
     * @param token
     * @return
     */
    public boolean isNeedUpdate(String token) {
        //获取token过期时间
        Object claim = JWTUtil.parseToken(token.replace(jwtProperties.getTokenPrefix(), "")).getPayload().getClaim(JWTPayload.EXPIRES_AT);
        long expireTime = DateUtil.parseDateTime(claim.toString()).getTime();
        //如果剩余过期时间少于过期时常的一半时 需要更新
        return (expireTime - DateUtil.current()) < (expireTime >> 1);
    }

    /**
     * 刷新token
     *
     * @param token
     * @return
     */
    public String refreshToken(String token, Map<String, Object> payload) {
        String replace = token.replace(jwtProperties.getTokenPrefix(), "");
        if (!validateToken(replace)) {
            throw new IllegalStateException("Invalid token");
        }
        if (isNeedUpdate(replace)) {
            return createToken(payload);
        }
        return token;
    }

    /**
     * 刷新token
     *
     * @param token
     * @param key
     * @return
     */
    public String refreshToken(String token, Map<String, Object> payload, String key) {
        String replace = token.replace(jwtProperties.getTokenPrefix(), "");
        if (!validateToken(replace, key)) {
            throw new IllegalStateException("Invalid token");
        }
        if (isNeedUpdate(replace)) {
            return createToken(payload, key);
        }
        return token;
    }
}
